// / <binding BeforeBuild='default' />
/* eslint-env node */
/* eslint camelcase: 'off' */
module.exports = function (grunt)
{
    var loadConfig = require('load-grunt-config');

    loadConfig(grunt, {
        configPath: __dirname + '/tasks/options',
        config: {
            target_dir: 'build',
            release_dir: 'build',
            release_custom_dir: 'build/custom',
            compile_dir: 'dist',
            modules_dir: 'dist/modules',
            docs_dir: 'docs',
            sourcemap: false,
            filename: 'phaser',
            pixiFilename: 'pixi.js',
            p2Filename: 'p2.js',
            creatureFilename: 'creature.js',
            filelist: [],
            banner: require('fs').readFileSync(__dirname + '/tasks/banner.txt', 'utf8')
        }
    });

    grunt.loadTasks('tasks');

    grunt.registerTask('default', [ 'build' ]);

    grunt.registerTask('docs', [ 'clean:docs', 'clean:out', 'gitclone:plugins', 'jsdoc:html', 'clean:out' ]);
    grunt.registerTask('tsdocs', [ 'ts:defs', 'clean:out', 'gitclone:plugins', 'jsdoc:json', 'buildtsdoc:pixi', 'buildtsdoc:phaser', 'replace:phasertsdefheader' ]);

    grunt.registerTask('custom', 'Build a custom version of Phaser', function ()
    {
        var modules = {
            intro: { description: 'Phaser UMD wrapper', optional: true, stub: false },
            phaser: { description: 'Phaser Globals', optional: false, stub: false },
            geom: { description: 'Geometry Classes', optional: false, stub: false },
            core: { description: 'Phaser Core', optional: false, stub: false },
            input: { description: 'Input Manager + Mouse and Touch Support', optional: false, stub: false },
            gamepad: { description: 'Gamepad Input', optional: true, stub: false },
            keyboard: { description: 'Keyboard Input', optional: true, stub: false },
            components: { description: 'Game Object Components', optional: false, stub: false },
            gameobjects: { description: 'Core Game Objects', optional: false, stub: false },
            bitmapdata: { description: 'BitmapData Game Object', optional: true, stub: false },
            graphics: { description: 'Graphics and PIXI Mask Support', optional: true, stub: false },
            rendertexture: { description: 'RenderTexture Game Object', optional: true, stub: false },
            text: { description: 'Text Game Object (inc. Web Font Support)', optional: true, stub: false },
            bitmaptext: { description: 'BitmapText Game Object', optional: true, stub: false },
            retrofont: { description: 'Retro Fonts Game Object', optional: true, stub: false },
            rope: { description: 'Rope and Strip Game Object', optional: true, stub: false },
            tilesprite: { description: 'Tile Sprite Game Object', optional: true, stub: true },
            system: { description: 'System Classes', optional: false, stub: false },
            math: { description: 'Math, QuadTree and RND', optional: false, stub: false },
            net: { description: 'Network Class', optional: true, stub: true },
            tweens: { description: 'Tween Manager', optional: true, stub: true },
            time: { description: 'Time and Clock Manager', optional: false, stub: false },
            animation: { description: 'Animation and Frame Manager', optional: false, stub: false },
            loader: { description: 'Loader and Cache', optional: false, stub: false },
            sound: { description: 'Sound Support (Web Audio and HTML Audio)', optional: true, stub: true },
            scale: { description: 'Scale and Full Screen Manager', optional: true, stub: true },
            debug: { description: 'Debug Class', optional: true, stub: true },
            dom: { description: 'DOM Utilities', optional: true, stub: true },
            utils: { description: 'Core Utilities', optional: false, stub: false },
            create: { description: 'Create Support', optional: true, stub: true },
            flexgrid: { description: 'Flex Grid and Flex Layer', optional: true, stub: false },
            color: { description: 'Color Functions', optional: true, stub: true },
            physics: { description: 'Physics Manager', optional: false, stub: false },
            arcade: { description: 'Arcade Physics', optional: true, stub: false },
            ninja: { description: 'Ninja Physics', optional: true, stub: false },
            p2: { description: 'P2 Physics', optional: true, stub: false },
            tilemaps: { description: 'Tilemap Support', optional: true, stub: false },
            particles: { description: 'Arcade Physics Particle System', optional: true, stub: true },
            weapon: { description: 'Arcade Physics Weapon Plugin', optional: true, stub: false },
            creature: { description: 'Creature Animation Tool Support', optional: true, stub: false },
            video: { description: 'Video Game Object', optional: true, stub: false },
            pixidefs: { description: 'Pixi defaults', optional: true, stub: false },
            outro: { description: 'Phaser UMD closure', optional: true, stub: false }
        };

        grunt.log.writeln('---------------------');
        grunt.log.writeln('Building Phaser ' + grunt.config.get('package.version'));
        grunt.log.writeln('---------------------');

        if (!grunt.option('exclude'))
        {
            grunt.log.writeln('\nUse --exclude to select which modules to exclude:\n');

            for (var module in modules)
            {
                if (modules[module].optional)
                {
                    grunt.log.writeln(module + ' - ' + modules[module].description);
                }
            }

            grunt.log.writeln('\nFor example: --exclude=p2,tilemaps,retrofont\n');
            grunt.log.writeln('Optional flags:\n');
            grunt.log.writeln('--filename yourfilename (builds to your own custom file name)');
            grunt.log.writeln('--sourcemap true (creates a source map)');
            grunt.log.writeln('--split true (splits Phaser, PIXI, p2 and Creature into separate files)');
            grunt.log.writeln('--uglify true (runs Uglify on the output files)');
            grunt.log.writeln('--verbose true (print some extra information)');
            grunt.log.writeln('\nNote that some modules have dependencies on others.\n');

            grunt.fail.fatal('No build options were specified.');
        }
        else
        {
            //  Defaults
            grunt.config.set('sourcemap', false);
            grunt.config.set('filename', 'phaser');
            grunt.config.set('split', false);
            grunt.config.set('target_dir', '<%= release_dir %>');

            var split = false;

            //  Overrides
            if (grunt.option('filename'))
            {
                grunt.config.set('filename', grunt.option('filename'));
            }

            if (grunt.option('sourcemap'))
            {
                grunt.config.set('sourcemap', grunt.option('sourcemap'));
            }

            if (grunt.option('split'))
            {
                grunt.config.set('split', grunt.option('split'));
                split = grunt.option('split');
            }

            grunt.log.writeln('Excluding modules:\n');

            var excludedKeys = [];

            //  Nothing is excluded!
            var excludes = false;

            if (grunt.option('exclude') !== 'null')
            {
                excludes = grunt.option('exclude').split(',');

                //  Check the given modules are all valid
                for (var i = 0; i < excludes.length; i++)
                {
                    var exclude = excludes[i];

                    if (modules[exclude])
                    {
                        grunt.log.writeln('* ' + exclude + ' - ' + modules[exclude].description);
                        excludedKeys[exclude] = true;
                    }
                    else
                    {
                        grunt.fail.fatal('Unknown module \'' + exclude + '\'');
                    }
                }

                //  Handle basic dependencies

                if (excludedKeys.arcade && !excludedKeys.particles)
                {
                    grunt.log.writeln('Warning: Particles rely on Arcade Physics which has been excluded. Removing Particles from build.');
                    excludes.push('particles');
                }

                if (excludedKeys.arcade && !excludedKeys.weapon)
                {
                    grunt.log.writeln('Warning: The Weapon Plugin relies on Arcade Physics which has been excluded. Removing Weapon Plugin from build.');
                    excludes.push('weapon');
                }

                if (excludedKeys.rendertexture && !excludedKeys.retrofont)
                {
                    grunt.log.writeln('Warning: RetroFonts rely on RenderTextures. Excluding from build.');
                    excludes.push('retrofont');
                }
            }

            /*
             * ///////////////////////////////////////////////////////////////////////
             *  Ok we know the excludes array is fine, let's get this show started //
             * ///////////////////////////////////////////////////////////////////////
             */

            var filelist = [];
            var pixiFilelist = [];

            //  Clean the working folder
            var tasks = [ 'clean:build' ];

            if (split)
            {
                /*
                 * //////////////////////////////////////
                 *  Split build (for Browserify, etc) //
                 * //////////////////////////////////////
                 */

                grunt.log.writeln('\nSplitting Globals ...\n');

                //  1) Creature

                if (!excludedKeys.creature)
                {
                    grunt.log.writeln('-> Creature');
                    tasks.push('concat:creatureGlobalSplit');

                    if (grunt.option('uglify'))
                    {
                        tasks.push('uglify:creature');
                    }
                }

                //  2) P2

                if (!excludedKeys.p2)
                {
                    grunt.log.writeln('-> P2.js');
                    tasks.push('concat:p2GlobalSplit');

                    if (grunt.option('uglify'))
                    {
                        tasks.push('uglify:p2');
                    }
                }

                //  3) PIXI

                grunt.log.writeln('-> PIXI');

                if (!excludedKeys.intro)
                {
                    tasks.push('concat:pixiIntro');
                    pixiFilelist.push('<%= modules_dir %>/pixi-intro.js');
                }

                tasks.push('concat:pixiMain');
                pixiFilelist.push('<%= modules_dir %>/pixi-main.js');

                /*
                 * //  Optional Rope
                 * if (!excludedKeys['rope'])
                 * {
                 * grunt.log.writeln("-> PIXI.Rope");
                 * tasks.push('concat:pixiRope');
                 * pixiFilelist.push('<%= modules_dir %>/pixi-rope.js');
                 * }
                 *
                 * //  Optional Tilesprite
                 * if (!excludedKeys['tilesprite'])
                 * {
                 * grunt.log.writeln("-> PIXI.TileSprite");
                 * tasks.push('concat:pixiTileSprite');
                 * pixiFilelist.push('<%= modules_dir %>/pixi-tilesprite.js');
                 * }
                 */

                //  PIXI Outro
                if (!excludedKeys.outro)
                {
                    tasks.push('concat:pixiOutro');
                    pixiFilelist.push('<%= modules_dir %>/pixi-outro.js');
                }

                grunt.config.set('pixiFilelist', pixiFilelist);

                tasks.push('concat:pixi');

                if (grunt.option('uglify'))
                {
                    tasks.push('uglify:pixi');
                }
            }
            else
            {
                /*
                 * /////////////////
                 *  Single build //
                 * /////////////////
                 */

                grunt.log.writeln('\nPackaging Globals ...\n');

                //  Prepare the globals first, the libs that live outside of Phaser

                //  1) Creature

                if (!excludedKeys.creature)
                {
                    grunt.log.writeln('-> Creature');
                    tasks.push('concat:creatureGlobal');
                    filelist.push('<%= modules_dir %>/creature-global.js');
                }

                //  2) P2

                if (!excludedKeys.p2)
                {
                    grunt.log.writeln('-> P2.js');
                    tasks.push('concat:p2Global');
                    filelist.push('<%= modules_dir %>/p2-global.js');
                }

                //  3) PIXI

                grunt.log.writeln('-> PIXI');

                if (!excludedKeys.intro)
                {
                    tasks.push('concat:pixiIntro');
                    filelist.push('<%= modules_dir %>/pixi-intro.js');
                }

                tasks.push('concat:pixiMain');
                filelist.push('<%= modules_dir %>/pixi-main.js');

                /*
                 * //  Optional Rope
                 * if (!excludedKeys['rope'])
                 * {
                 * grunt.log.writeln("-> PIXI.Rope");
                 * tasks.push('concat:pixiRope');
                 * filelist.push('<%= modules_dir %>/pixi-rope.js');
                 * }
                 *
                 * //  Optional Tilesprite
                 * if (!excludedKeys['tilesprite'])
                 * {
                 * grunt.log.writeln("-> PIXI.TileSprite");
                 * tasks.push('concat:pixiTileSprite');
                 * filelist.push('<%= modules_dir %>/pixi-tilesprite.js');
                 * }
                 */

                //  PIXI Outro
                if (!excludedKeys.outro)
                {
                    tasks.push('concat:pixiOutro');
                    filelist.push('<%= modules_dir %>/pixi-outro.js');
                }
            }

            //  And now for Phaser

            grunt.log.writeln('\nBuilding ...');

            if (excludes !== false)
            {
                for (var key in modules)
                {
                    if (modules[key].stub && excludes.indexOf(key) !== -1)
                    {
                        //  If the module IS excluded and has a stub, we need that
                        tasks.push('concat:' + key + 'Stub');

                        filelist.push('<%= modules_dir %>/' + key + '.js');
                    }
                    else if (modules[key].optional === false || excludes.indexOf(key) === -1)
                    {
                        //  If it's required or NOT excluded, add it to the tasks list
                        tasks.push('concat:' + key);

                        filelist.push('<%= modules_dir %>/' + key + '.js');

                        //  Special case: If they have Arcade Physics AND Tilemaps we need to include the Tilemap Collision class
                        if (key === 'arcade' && !excludes.tilemaps)
                        {
                            tasks.push('concat:arcadeTilemaps');
                            filelist.push('<%= modules_dir %>/arcadeTilemaps.js');
                        }
                    }
                }
            }
            else
            {
                //  The full monty ...

                for (var mkey in modules)
                {
                    tasks.push('concat:' + mkey);
                    filelist.push('<%= modules_dir %>/' + mkey + '.js');
                }
            }

            grunt.config.set('filelist', filelist);

            tasks.push('concat:custom');

            if (grunt.option('uglify'))
            {
                tasks.push('uglify:custom');
            }

            if (grunt.option('copy'))
            {
                if (grunt.option('copycustom'))
                {
                    grunt.config.set('target_dir', '<%= release_custom_dir %>');
                }
                tasks.push('copy:custom');
            }

            grunt.task.run(tasks);
        }
    });

    grunt.registerTask('dist', 'Compile all Phaser versions and copy to the build folder', function ()
    {
        grunt.task.run('clean:release');
        grunt.task.run('full');
        grunt.task.run('arcadephysics');
        grunt.task.run('nophysics');
        grunt.task.run('minimum');
        grunt.task.run('split');
    });

    grunt.registerTask('release', 'Compile all Phaser versions to the build folder and update docs and defs', function ()
    {
        grunt.task.run('clean:release');
        grunt.task.run('full');
        grunt.task.run('arcadephysics');
        grunt.task.run('nophysics');
        grunt.task.run('minimum');
        grunt.task.run('split');
        grunt.task.run('creature');
        grunt.task.run('docs');
        grunt.task.run('tsdocs');
    });

    grunt.registerTask('build', 'Compile all Phaser versions just to the temporary dist folder', function ()
    {
        grunt.option('exclude', 'ninja,creature');
        grunt.option('filename', 'phaser');
        grunt.option('sourcemap', true);
        grunt.option('copy', false);
        grunt.option('uglify', true);

        grunt.task.run('custom');
    });

    grunt.registerTask('full', 'Phaser (excluding Ninja and Creature)', function ()
    {
        grunt.option('exclude', 'ninja,creature');
        grunt.option('filename', 'phaser');
        grunt.option('sourcemap', true);
        grunt.option('copy', true);
        grunt.option('uglify', true);

        grunt.task.run('custom');
    });

    grunt.registerTask('complete', 'Phaser Build with all libs', function ()
    {
        grunt.option('exclude', 'null');
        grunt.option('filename', 'phaser-complete');
        grunt.option('sourcemap', false);
        grunt.option('copy', true);
        grunt.option('copycustom', true);
        grunt.option('uglify', true);

        grunt.task.run('custom');
    });

    grunt.registerTask('split', 'Compile Phaser to dist folder and splits the globals into single files', function ()
    {
        grunt.option('exclude', 'ninja,creature');
        grunt.option('filename', 'phaser-split');
        grunt.option('sourcemap', true);
        grunt.option('copy', true);
        grunt.option('copycustom', true);
        grunt.option('uglify', true);
        grunt.option('split', true);

        grunt.task.run('custom');
    });

    grunt.registerTask('test', 'Phaser Test Build (all libs)', function ()
    {
        grunt.option('exclude', 'ninja,creature');
        grunt.option('filename', 'phaser-test');
        grunt.option('sourcemap', false);
        grunt.option('copy', false);
        grunt.option('uglify', false);

        grunt.task.run('custom');
    });

    grunt.registerTask('uglytest', 'Phaser Test Build (all libs)', function ()
    {
        grunt.option('exclude', 'ninja,creature');
        grunt.option('filename', 'phaser-test');
        grunt.option('sourcemap', false);
        grunt.option('copy', false);
        grunt.option('uglify', true);

        grunt.task.run('custom');
    });

    grunt.registerTask('creature', 'Phaser + Creature', function ()
    {
        grunt.option('exclude', 'ninja');
        grunt.option('filename', 'phaser-creature');
        grunt.option('sourcemap', true);
        grunt.option('copy', true);
        grunt.option('copycustom', true);
        grunt.option('uglify', true);

        grunt.task.run('custom');
    });

    grunt.registerTask('arcadephysics', 'Phaser with Arcade Physics, Tilemaps, Weapons and Particles', function ()
    {
        grunt.option('exclude', 'ninja,p2,creature');
        grunt.option('filename', 'phaser-arcade-physics');
        grunt.option('sourcemap', true);
        grunt.option('copy', true);
        grunt.option('copycustom', true);
        grunt.option('uglify', true);

        grunt.task.run('custom');
    });

    grunt.registerTask('ninjaphysics', 'Phaser with Ninja Physics and Tilemaps', function ()
    {
        grunt.option('exclude', 'p2,particles,creature');
        grunt.option('filename', 'phaser-ninja-physics');
        grunt.option('sourcemap', true);
        grunt.option('copy', true);
        grunt.option('copycustom', true);
        grunt.option('uglify', true);

        grunt.task.run('custom');
    });

    grunt.registerTask('nophysics', 'Phaser without physics, tilemaps or particles', function ()
    {
        grunt.option('exclude', 'arcade,ninja,p2,tilemaps,particles,creature,weapon');
        grunt.option('filename', 'phaser-no-physics');
        grunt.option('sourcemap', true);
        grunt.option('copy', true);
        grunt.option('copycustom', true);
        grunt.option('uglify', true);

        grunt.task.run('custom');
    });

    grunt.registerTask('minimum', 'Phaser without any optional modules', function ()
    {
        grunt.option('exclude', 'gamepad,keyboard,bitmapdata,graphics,rendertexture,text,bitmaptext,retrofont,net,tweens,sound,debug,arcade,ninja,p2,tilemaps,particles,creature,video,rope,tilesprite,weapon');
        grunt.option('filename', 'phaser-minimum');
        grunt.option('sourcemap', true);
        grunt.option('copy', true);
        grunt.option('copycustom', true);
        grunt.option('uglify', true);

        grunt.task.run('custom');
    });
};
